// fields.cpp
// Customizer_Base
// Protea Customizer/Customizer Pro / Business Logic for managing fields
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// Please send correspondence to:
//
// theproteaproject@gmail.com
//

#include "base_setup.h"

// Include specific field defns
#include "../../../Fields/field_defns.h"

//@ SEC 3
//@ CHA Field Descriptor Verify
//@ COD FieldDescVerify
//@ TAB FieldDescriptor Read
//@ GEN Use FieldDescVerify to verify field descriptor record

void FieldDescVerify( XCBuffer* input, verify_type verify_mode, XCTransactionVars* vars, bool pro_mode, XCBuffer* orig ) {
	TRANS_INFUNC( FieldDescVerify );

	//@ SUB ValidateName to check for a valid name
	XCBuffer* name = input;
	ValidateName( FLD_FIELD_NAME, name, ERR_FIELD, vars );

	bool exists;
	if (orig)
		exists = LookupNumberFromName( vars, orig, orig, FLD_FIELD_NUMBER, FLD_FIELD_NAME, FMT_FIELD_DESCRIPTOR );
	else
		exists = LookupNumberFromName( vars, name, name, FLD_FIELD_NUMBER, FLD_FIELD_NAME, FMT_FIELD_DESCRIPTOR );

	//@ If verifying an ADD transaction,
	if (verify_mode == VERIFY_ADD || verify_mode == VERIFY_COPY) {
		//@ GRS

		//@ If this record already exists,
		//@ ERR C (ALREADY_EXISTS) Field already exists
		if ( exists )
			vars->Errors->Add( ERR_ALREADY_EXISTS, SEV_CRITICAL, FLD_FIELD_NAME, 1, ERR_FIELD );

		//@ GRE
	} else { // Review, Change or delete
		//@ GRS

		//@ If this record doesn't exist,
		//@ ERR E (DOESNT_EXIST) Field doesn't exist
		if (!exists)
			vars->Errors->Add( ERR_DOESNT_EXIST, SEV_CRITICAL, FLD_FIELD_NAME, 1, ERR_FIELD );

		//@ GRE
	};

	//@ If verifying an ADD or CHANGE transaction,
	if (verify_mode == VERIFY_ADD || verify_mode == VERIFY_CHANGE || verify_mode == VERIFY_COPY) {
		//@ GRS
	
		//@ Check data type range
		//@ ERR E (DATA_TYPE)
		long type = input->GetField( FLD_DATA_TYPE )->GetLong( true );
		if (type < MIN_TYPE || type > MAX_TYPE)
			vars->Errors->Add( ERR_DATA_TYPE, SEV_ERROR, FLD_DATA_TYPE );

        const tdata_type_defnition* data_type = GetDataTypeDefn( type );

		//@ Check for invalid data types
		//@ ERR E (DISP_DATA_TYPE_INV)
		if (type == DISPLAY_NUM)
			vars->Errors->Add( ERR_DISP_DATA_TYPE_INV, SEV_ERROR, FLD_DATA_TYPE );

		//@ All datatypes, other than VAR_STRING and BLOB have a max length limit
		if (type != VAR_STRING && type != XCBLOB) {
			//@ GRS

			//@ For a field descriptor, check for the enforced size:
			if (data_type->enforced_size)
				input->GetField( FLD_DB_BYTE_LENGTH )->SetLong( data_type->enforced_size );

			//@ Check that max length is in the range of 0-254 (zero max length means use field_descriptor default)
			//@ ERR E (ERR_INV_SHORT_RANGE) Must be in the range of 0 to 254
			if (!input->GetField( FLD_DB_BYTE_LENGTH )->CheckInRange( data_type->min_size, data_type->max_size ))
				vars->Errors->Add( ERR_INV_SHORT_RANGE, SEV_ERROR, FLD_DB_BYTE_LENGTH, 3, input->GetField( FLD_DB_BYTE_LENGTH ), data_type->min_size, data_type->max_size );

			//@ Zero is an invalid length
			if (input->GetField( FLD_DB_BYTE_LENGTH )->Compare(0) <= 0)
				vars->Errors->Add( ERR_FIELD_LENGTH, SEV_ERROR, FLD_DB_BYTE_LENGTH );

			//@ GRE
		} else {
			//@ Otherwise, it must be greater than zero
			//@ GRS
			//@ Ensure default field length > 0
			//@ ERR E (FIELD_LENGTH)
			if (input->GetField( FLD_DB_BYTE_LENGTH )->Compare(0) <= 0)
				vars->Errors->Add( ERR_FIELD_LENGTH, SEV_ERROR, FLD_DB_BYTE_LENGTH );
			//@ GRE
		};

		//@ Ensure decimal places >= 0
		//@ ERR E (DECIMAL_PLACES)
		if (input->GetField( FLD_DECIMAL_PLACES )->Compare(0) < 0)
			vars->Errors->Add( ERR_DECIMAL_PLACES, SEV_ERROR, FLD_DECIMAL_PLACES );

		//@ *** Check default value

		//@ GRE
	};


	//@ If verifying a CHANGE transaction,
	if (verify_mode == VERIFY_CHANGE) {
		//@ GRS

		//@ Find all values that changed
		input->MarkChanges( orig );

		//@ Mark field number as not changed
		input->GetField( FLD_FIELD_NUMBER )->SetChange( false );

		//@ Ensure that Critical field not changed
		if (!pro_mode && input->GetField( FLD_CRITICAL_FIELD )->IsChanged())
			RAISETOOLSERROR( ERR_CRIT_VALUE_CHANGE );
	};


	//@ If verifying a CHANGE or DELETE transaction, check critical field flags (NON-DESIGNER ONLY)
	if (!pro_mode && (verify_mode == VERIFY_CHANGE || verify_mode == VERIFY_DELETE)) {
		//@ GRS

		//@ Check that this data structure is not a critical field
		//@ ERR C (CRITICAL_FIELD) Cannot change/delete critical field
		//@ ERR C (CRITICAL_FIELD_DEL) Cannot delete critical field
		int result = input->GetField( FLD_CRITICAL_FIELD )->Compare(1);
		if (result > 0)		// Critical field >= 2
			vars->Errors->Add( ERR_CRITICAL_FIELD, SEV_CRITICAL );
		else if (result == 0 && verify_mode == VERIFY_DELETE)
			vars->Errors->Add( ERR_CRITICAL_FIELD_DEL, SEV_CRITICAL );

		//@ GRE
	};

	//@ If verifying a DELETE transaction,
	if (verify_mode == VERIFY_DELETE) {
		//@ GRS
		//@ SUB DataStructuresReviewFormat check for data structures records, make sure that the field is not in use
		//@ ERR E (FIELD_IN_USE)
		XCBuffer* ds = NULL;
		XCDBStatement* stmt = NULL;
		try {
			ds = App->CreateBuffer( FMT_DATA_STRUCTURES, false );
			ds->MoveCorresponding( input, vars->Errors );
			stmt = DataStructureReviewField( ds, vars );
			if (stmt && !stmt->Eof())
				vars->Errors->Add( ERR_FIELD_IN_USE, SEV_ERROR, FLD_FIELD_NAME );
			App->CleanupObjects( 2, &stmt, &ds );
		} catch( ... ) {
			App->CleanupObjects( 2, &stmt, &ds );
			throw;
		};
		//@ GRE
	};

	TRANS_OUTFUNC();
};






//@ SEC 3
//@ CHA Field Descriptor Retrieve
//@ COD FieldDescRetrieve
//@ TAB FieldDescriptor Read
//@ GEN Use FieldDescRetrieve to retrieve field descriptor record

bool FieldDescRetrieve( XCBuffer* fielddesc, XCTransactionVars* vars ) {
	TRANS_INFUNC( FieldDescRetrieve );

	XCDBStatement* stmt = NULL;
	XCSQLGen* query = vars->query;
	bool result;

	//@ Attempt to read FIELD_STRUCTURE record
	try {
		query->MakeFull( SQL_STMT_SELECT, fielddesc );
		stmt = vars->dbtxn->ExecuteSQL( query );
		result = !stmt->Eof();
		App->CleanupObjects( 1, &stmt );
	} catch( ... ) {
		App->CleanupObjects( 1, &stmt );
		throw;
	};

	TRANS_OUTFUNCRET( result );
};






//@ SEC 3
//@ CHA Field Descriptor Add
//@ COD FieldDescAdd
//@ TAB FieldDescriptor Write
//@ GEN Use FieldDescAdd to add a new field descriptor record
//@ GEN Attempts to use numbers from the field nbr pool first,
//@ GEN if not, updates APPL_DEFN.

void FieldDescAdd( XCBuffer* input, XCTransactionVars* vars, bool pro_mode ) {
	XCDBStatement* stmt = NULL;
	XCBuffer* field_pool = NULL;
	XCBuffer* field_desc = NULL;

	XCSQLGen* query = vars->query;

	XCField* fields[3];
	XCCompare comptemp[2];

	TRANS_INFUNC( FieldDescAdd );

	try {
		//@ GRS Determine the next available field number
		//@ Attempt to read records from FIELD_NBR_POOL. If we are adding a system field,
		//@ NOB look for negative pool numbers, otherwise look for positive field numbers.
		field_pool = App->CreateBuffer( FMT_FIELD_NBR_POOL, false );
		field_pool->MoveField( input, FLD_APPL_NBR );
		fields[0] = field_pool->GetField( FLD_APPL_NBR );
		fields[1] = field_pool->GetField( FLD_FIELD_NUMBER );
		fields[1]->Clear();
		fields[2] = NULL;
		comptemp[1].comparison = pro_mode ? cmp_greaterthan : cmp_lessthan;

		query->MakeSELECT( field_pool );
		query->AddWHERE( fields, comptemp );
		stmt = vars->dbtxn->ExecuteSQL( query );
		if (!stmt->Eof()) {
			//@ GRS If a field pool record exists:
			//@ Move the field number to the input buffer
			input->MoveField( field_pool, FLD_FIELD_NUMBER );

			//@ Delete the field pool record
			query->MakeFull( SQL_STMT_DELETE, field_pool );
			vars->dbtxn->ExecuteSQL( query, SQL_NO_RESULT );
			//@ GRE
		} else {
			//@ GRS If no field pool records exist:

			//@ Grab the next available field number
			field_desc = App->CreateBuffer( FMT_FIELD_DESCRIPTOR, false );
			field_desc->MoveCorresponding( input, vars->Errors );

			XCField* list[3];
			XCCompare comp[2];
			list[0] = field_desc->GetField( FLD_APPL_NBR );
			list[1] = field_desc->GetField( FLD_FIELD_NUMBER );
			list[1]->Clear();
			list[2] = NULL;

			XCOrder order;
			order.field = list[1];
			if (pro_mode) {
				order.direction = order_desc;
				comp[1].comparison = cmp_greaterthan;
			} else {
				order.direction = order_asc;
				comp[1].comparison = cmp_lessthan;
			};

			query->MakeSELECT( field_desc );
			query->AddWHERE( list, comp ); 
			query->AddORDERBY( &order );
			vars->dbtxn->ExecuteSQL( query, SQL_NO_RESULT );

			//@ Set next field number (either plus or minus)
			if (pro_mode)
				field_desc->AddConst( FLD_FIELD_NUMBER, 1 );
			else
				field_desc->SubConst( FLD_FIELD_NUMBER, 1 );

			//@ Move the field number from the APPL_DEFN record to the input buffer
			input->MoveField( field_desc, FLD_FIELD_NUMBER );

			//@ GRE
		};

		assert( input->GetField( FLD_FIELD_NUMBER )->GetLong() );

		//@ Insert the new FIELD_DESCRIPTOR record
		query->MakeFull( SQL_STMT_INSERT, input );
		vars->dbtxn->ExecuteSQL( query, SQL_NO_RESULT );
	//@ GRE 
	} catch( ... ) {
		App->CleanupObjects( 3, &stmt, &field_pool, &field_desc );
		throw;
	};
	App->CleanupObjects( 3, &stmt, &field_pool, &field_desc );

	TRANS_OUTFUNC();
};



//@ SEC 3
//@ CHA Field Descriptor Change
//@ COD FieldDescChange
//@ TAB FieldDescriptor Write
//@ GEN Use FieldDescChange to update a field record

void FieldDescChange( XCBuffer* input, XCBuffer* orig, XCTransactionVars* vars ) {
	XCBuffer* dest = NULL;
	XCBuffer* ds = NULL;
	XCBuffer* fd = NULL;
	XCBuffer* relate = NULL;
	XCDBStatement* stmt = NULL;

	XCSQLGen* query = vars->query;

	TRANS_INFUNC( FieldDescChange );

	//@ Issue update statement
	query->MakeFull( SQL_STMT_UPDATE, input, orig );
	vars->dbtxn->ExecuteSQL( query, SQL_NO_RESULT );

	//@ Update Data_structure records
	ds = vars->myapp->CreateBuffer( FMT_DATA_STRUCTURES );
	fd = vars->myapp->CreateBuffer( FMT_FORMAT_DESCRIPTR );
	ds->MoveCorresponding( input, vars->Errors );
	stmt = GetFieldWhereUsed( ds, vars );
	while (!stmt->Eof()) {
		fd->MoveCorresponding( ds, vars->Errors );
		if (FormatDescRetrieve( fd, vars )) {
			ds->MoveField( input, FLD_DATA_TYPE );
			FixFieldType( fd, input, ds, vars );
		};
		stmt->MoveNext();
	}; // while stmt
	stmt->Release();
	stmt = NULL;


	//@ Check for related fields
	relate = App->CreateBuffer( FMT_FIELD_RELATE, false );
	relate->MoveCorresponding( input, vars->Errors );
	relate->MoveUnlikeField( FLD_MSTR_FIELD_NBR, input, FLD_FIELD_NUMBER );

	dest = App->CreateBuffer( FMT_FIELD_DESCRIPTOR );
	try {
		stmt = FieldRelateFind( relate, vars );
		while (!stmt->Eof()) {
			bool changed = false;

			// Retrieve the relate field info record
			dest->MoveCorresponding( input, NULL );
			dest->MoveCorresponding( relate, NULL );
			FieldDescRetrieve( dest, vars );

			// Load the old descriptor record
			dest->MarkChanges( input );
			for (int i=0; i<dest->GetFieldCount() && !changed; i++) {
				XCField* temp = dest->GetFieldByIndex( i );
				if (temp->IsChanged()) {
					int number = temp->GetFieldNumber();
					if (number != FLD_FIELD_NAME && number != FLD_FIELD_NUMBER &&
						number != FLD_APPL_NBR && number != FLD_DESC_MESSAGE )
						changed = true;
				};
			};

			if (changed) {
				// Update the values of the field descriptor record
				XCBuffer* dest_orig = dest_orig = dest->Copy();
				dest->MoveCorresponding( input, vars->Errors );
				dest->MoveField( dest_orig, FLD_FIELD_NAME );
				dest->MoveField( dest_orig, FLD_FIELD_NUMBER );
				dest->MoveField( dest_orig, FLD_DESC_MESSAGE );
				dest->MarkChanges( dest_orig );
				FieldDescChange( dest, dest_orig, vars );
				dest_orig->Release();
			};

			stmt->MoveNext();
		};
	} catch( ... ) {
		// Ignore errors?
	};
	App->CleanupObjects( 5, &stmt, &dest, &ds, &fd, &relate );

	TRANS_OUTFUNC();
};





//@ SEC 3
//@ CHA Field Descriptor Delete
//@ COD FieldDescDelete
//@ TAB FieldDescriptor Write
//@ GEN Use FieldDescDelete to remove a field

void FieldDescDelete( XCBuffer* input, XCTransactionVars* vars ) {
	XCSQLGen* query = vars->query;
	XCBuffer* field_pool = NULL;
	XCBuffer* relate = NULL;

	TRANS_INFUNC( FieldDescDelete );

	try {
		//@ Delete FIELD_DESCRIPTOR record
		query->MakeFull( SQL_STMT_DELETE, input );
		vars->dbtxn->ExecuteSQL( query, SQL_NO_RESULT );

		//@ Insert record into FIELD_NBR_POOL
		field_pool = App->CreateBuffer( FMT_FIELD_NBR_POOL, false );
		field_pool->MoveCorresponding( input, vars->Errors );

		query->MakeFull( SQL_STMT_INSERT, field_pool );
		vars->dbtxn->ExecuteSQL( query, SQL_NO_RESULT );

		//@ Cleanup relation records
		relate = App->CreateBuffer( FMT_FIELD_RELATE, false );
		relate->MoveCorresponding( input, vars->Errors );
		FieldRelateDeleteField( relate, vars );
	} catch( ... ) {
		App->CleanupObjects( 2, &field_pool, &relate );
		throw;
	};
	App->CleanupObjects( 2, &field_pool, &relate );

	TRANS_OUTFUNC();
};





//@ SEC 3
//@ CHA Field Descriptor Delete All
//@ COD FieldDescDeleteAll
//@ TAB FieldDescriptor Write
//@ GEN Use FieldDescDeleteAll to remove fields
void FieldDescDeleteAll( XCBuffer* input, XCTransactionVars* vars ) {
	XCSQLGen* query = vars->query;
	XCBuffer* fields = NULL;
	XCBuffer* field_pool = NULL;

	TRANS_INFUNC( FieldDescDeleteAll );

	try {
		//@ Create a FIELD_DESCRIPTOR buffer, move in APPL_NBR
		fields = App->CreateBuffer( FMT_FIELD_DESCRIPTOR, false );
		fields->MoveField( input, FLD_APPL_NBR );

		//@ Delete FIELD_DESCRIPTOR records
		query->MakeDELETE( fields );
		query->AddWHERE( 1, fields->GetField( FLD_APPL_NBR ) );
		vars->dbtxn->ExecuteSQL( query, SQL_NO_RESULT );

		//@ Create a FIELD_NBR_POOL buffer, move in APPL_NBR
		field_pool = App->CreateBuffer( FMT_FIELD_NBR_POOL, false );
		field_pool->MoveField( input, FLD_APPL_NBR );

		//@ Delete FIELD_NBR_POOL records
		query->MakeDELETE( field_pool );
		query->AddWHERE( 1, field_pool->GetField( FLD_APPL_NBR ) );
		vars->dbtxn->ExecuteSQL( query, SQL_NO_RESULT );
	} catch( ... ) {
		App->CleanupObjects( 2, &fields, &field_pool );
		throw;
	};
	App->CleanupObjects( 2, &fields, &field_pool );

	TRANS_OUTFUNC();
};




//////////////////////////////////////////////////////////
//
// Internal functions
//
//////////////////////////////////////////////////////////

//@ SEC 2
//@ CHA AddFieldInternal
//@ COD AddFieldInternal
void AddFieldInternal( XCTransactionVars* vars, bool pro_mode ) {
	//@ Create a transaction buffer
	XCBuffer* TransBuf = vars->myapp->CreateBuffer( FMT_FIELD_DESCRIPTOR, false );

	try {
		//@ Move supplied buffer information into transaction buffer
		TransBuf->MoveCorresponding( vars->buffer, vars->Errors );

		if (vars->buffer->GetFormatNumber() != FMT_FIELD_DESCRIPTOR)
			TransBuf->GetField( FLD_FIELD_NUMBER )->Clear();

		//@ SUB FieldDescVerify to verify input data
		FieldDescVerify( TransBuf, VERIFY_ADD, vars, pro_mode );

		vars->Errors->Check();

		//@ SUB FieldDescAdd to add new record
		FieldDescAdd( TransBuf, vars, pro_mode );

		vars->buffer->MoveCorresponding( TransBuf, vars->Errors );
		vars->myapp->TransResponse( vars->result, vars->buffer, TRANS_HEADER_TYPE );
	} catch( ... ) {
		TransBuf->Release();
		throw;
	};
	TransBuf->Release();
};



//@ SEC 2
//@ CHA ChgFieldInternal
//@ COD ChgFieldInternal
void ChgFieldInternal( XCTransactionVars* vars, bool pro_mode ) {
	XCBuffer* TransBuf = NULL;
	XCBuffer* Old_Buffer = NULL;

	try {
		//@ Create the transaction buffer
		TransBuf = vars->myapp->CreateBuffer( FMT_FIELD_DESCRIPTOR, true );

		//@ Create a copy of the transaction buffer
		Old_Buffer = TransBuf->Copy();

		//@ Move screen data into copy buffer
		Old_Buffer->MoveCorresponding( vars->buffer, vars->Errors, true );

		//@ SUB FieldDescRetrieve Load the old record into the copy buffer
		if (!FieldDescRetrieve( Old_Buffer, vars ))
			vars->Errors->Add( ERR_DOESNT_EXIST, SEV_CRITICAL, 0, 1, ERR_FIELD );

		//@ Overlay screen data
		TransBuf->MoveCorresponding( Old_Buffer, vars->Errors, true );
		TransBuf->MoveCorresponding( vars->buffer, vars->Errors, true );

		//@ Check for errors.
		vars->Errors->Check();

		//@ SUB FieldDescValidate to validate input data
		FieldDescVerify( TransBuf, VERIFY_CHANGE, vars, pro_mode, Old_Buffer );

		//@ Check for errors.
		vars->Errors->Check();

		//@ SUB FieldDescChange change field record
		FieldDescChange( TransBuf, Old_Buffer, vars );
	} catch( ... ) {
		vars->myapp->CleanupObjects( 2, &TransBuf, &Old_Buffer );
		throw;
	};
	vars->myapp->CleanupObjects( 2, &TransBuf, &Old_Buffer );
};



//@ SEC 2
//@ CHA DelFieldInternal
//@ COD DelFieldInternal
void DelFieldInternal( XCTransactionVars* vars, bool pro_mode ) {
	//@ Create a transaction buffer
	XCBuffer* TransBuf = NULL;

	try {
		TransBuf = vars->myapp->CreateBuffer( FMT_FIELD_DESCRIPTOR, false );
		TransBuf->MoveCorresponding( vars->buffer, vars->Errors );

		//@ SUB FieldDescVerify to verify valid deletion
		FieldDescVerify( TransBuf, VERIFY_DELETE, vars, pro_mode );
		vars->Errors->Check();

		//@ SUB FieldDescDel to delete field record
		FieldDescDelete( TransBuf, vars );
	} catch( ... ) {
		vars->myapp->CleanupObjects( 1, &TransBuf );
		throw;
	};
	vars->myapp->CleanupObjects( 1, &TransBuf );
};




//@ SEC 2
//@ CHA CopyFieldInternal
//@ COD CopyFieldInternal
void CopyFieldInternal( XCTransactionVars* vars, bool pro_mode ) {
	XCBuffer* SourceBuf = NULL;
	XCBuffer* DestBuf = NULL;
	XCBuffer* FieldInfo = NULL;

	try {
		//@ Create a FIELD_DESCRIPTOR buffer
		SourceBuf = vars->myapp->CreateBuffer( FMT_FIELD_DESCRIPTOR, true );
		DestBuf = vars->myapp->CreateBuffer( FMT_FIELD_DESCRIPTOR, true );

		//@ Move supplied buffer information into transaction buffer
		SourceBuf->MoveCorresponding( vars->buffer, vars->Errors, true );
		SourceBuf->MoveUnlikeField( FLD_FIELD_NAME, vars->buffer, FLD_FORMAT_NAME );

		//@ Check for errors.
		vars->Errors->Check();

		//@ SUB FieldDescValidate to validate input data
		FieldDescVerify( SourceBuf, VERIFY_REVIEW, vars, false );

		//@ SUB FieldDescRetrieve to obtain record data
		FieldDescRetrieve( SourceBuf, vars );

		//@ Now put in new field name
		DestBuf->MoveCorresponding( SourceBuf, vars->Errors );
		DestBuf->MoveCorresponding( vars->buffer, vars->Errors );
		DestBuf->MoveUnlikeField( FLD_FIELD_NAME, vars->buffer, FLD_TGT_FORMAT_NAME );
		DestBuf->GetField( FLD_FIELD_NUMBER )->Clear();
		DestBuf->DebugDump();
		FieldDescVerify( DestBuf, VERIFY_COPY, vars, false );
		vars->Errors->Check();

		//@ SUB FieldDescAdd to add the new record
		FieldDescAdd( DestBuf, vars, false );

		vars->buffer->MoveCorresponding( DestBuf, vars->Errors );
		vars->myapp->TransResponse( vars->result, vars->buffer, TRANS_HEADER_TYPE );

		FieldInfo = vars->myapp->CreateBuffer( FMT_ADD_FIELD, true );
		FieldInfo->MoveCorresponding( DestBuf, vars->Errors );
		vars->myapp->TransResponse( vars->result, FieldInfo, TRANS_DETAIL_TYPE );
	//@ GRE End the transaction
	} catch( ... ) {
		vars->myapp->CleanupObjects( 3, &SourceBuf, &DestBuf, &FieldInfo );
		throw;
	};
	vars->myapp->CleanupObjects( 3, &SourceBuf, &DestBuf, &FieldInfo );
};
